import tkinter as tk
from tkinter import filedialog, scrolledtext, messagebox, ttk
import re
import os


class BibTexSorterApp:
    def __init__(self, root):
        self.root = root
        self.root.title("BibTeX引用排序工具")
        self.root.geometry("800x600")

        self.tex_files = []
        self.bibitem_file = ""
        self.bibtex_id_list = []
        self.bibitem_dict = {}

        self.create_widgets()

    def create_widgets(self):
        # 主框架
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill="both", expand=True, padx=10, pady=10)

        # 文件选择区域
        file_frame = ttk.LabelFrame(main_frame, text="文件选择")
        file_frame.pack(fill="x", pady=10)

        # TEX文件选择
        ttk.Label(file_frame, text="已选择的TEX文件:").grid(row=0, column=0, sticky="w", padx=5, pady=5)

        # 带滚动条的文件列表
        file_list_frame = ttk.Frame(file_frame)
        file_list_frame.grid(row=1, column=0, columnspan=3, padx=5, pady=5, sticky="ew")

        self.file_listbox = tk.Listbox(file_list_frame, height=5, width=70)
        file_scrollbar = ttk.Scrollbar(file_list_frame, orient="vertical", command=self.file_listbox.yview)
        self.file_listbox.configure(yscrollcommand=file_scrollbar.set)

        self.file_listbox.pack(side="left", fill="both", expand=True)
        file_scrollbar.pack(side="right", fill="y")

        # 文件操作按钮
        file_btn_frame = ttk.Frame(file_frame)
        file_btn_frame.grid(row=2, column=0, pady=5, sticky="w")

        ttk.Button(file_btn_frame, text="添加TEX文件", command=self.add_tex_files).pack(side="left", padx=2)
        ttk.Button(file_btn_frame, text="删除所选", command=self.remove_selected_file).pack(side="left", padx=2)
        ttk.Button(file_btn_frame, text="上移", command=self.move_file_up).pack(side="left", padx=2)
        ttk.Button(file_btn_frame, text="下移", command=self.move_file_down).pack(side="left", padx=2)

        # BibItem文件选择
        ttk.Label(file_frame, text="所选BibItem文件:").grid(row=3, column=0, sticky="w", padx=5, pady=5)
        self.bibitem_file_text = tk.Entry(file_frame, width=70)
        self.bibitem_file_text.grid(row=4, column=0, padx=5, pady=5, sticky="ew")
        ttk.Button(file_frame, text="选择BibItem文件", command=self.select_bibitem_file).grid(row=4, column=1, padx=5)

        # 处理按钮
        ttk.Button(main_frame, text="处理文件", command=self.process_files).pack(pady=10)

        # 结果区域
        result_frame = ttk.LabelFrame(main_frame, text="排序后的BibItem条目")
        result_frame.pack(fill="both", expand=True, pady=10)

        # 输出文本区域
        self.result_text = scrolledtext.ScrolledText(result_frame, height=15, width=80)
        self.result_text.pack(fill="both", expand=True, padx=5, pady=5)

        # 保存按钮
        ttk.Button(main_frame, text="保存结果", command=self.save_results).pack(pady=5)

        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief="sunken", anchor="w")
        status_bar.pack(side="bottom", fill="x")

    def add_tex_files(self):
        files = filedialog.askopenfilenames(
            title="选择TEX文件",
            filetypes=[("TEX文件", "*.tex"), ("所有文件", "*.*")]
        )

        if files:
            for file in files:
                if file not in self.tex_files:
                    self.tex_files.append(file)
                    self.file_listbox.insert(tk.END, os.path.basename(file))

    def remove_selected_file(self):
        selected_indices = self.file_listbox.curselection()
        if not selected_indices:
            return

        # 从后向前删除以避免索引变化
        for i in sorted(selected_indices, reverse=True):
            del self.tex_files[i]
            self.file_listbox.delete(i)

    def move_file_up(self):
        selected_indices = self.file_listbox.curselection()
        if not selected_indices or selected_indices[0] == 0:
            return

        idx = selected_indices[0]
        self.tex_files[idx], self.tex_files[idx - 1] = self.tex_files[idx - 1], self.tex_files[idx]

        # 更新列表框
        filename = self.file_listbox.get(idx)
        self.file_listbox.delete(idx)
        self.file_listbox.insert(idx - 1, filename)
        self.file_listbox.selection_set(idx - 1)

    def move_file_down(self):
        selected_indices = self.file_listbox.curselection()
        if not selected_indices or selected_indices[0] == len(self.tex_files) - 1:
            return

        idx = selected_indices[0]
        self.tex_files[idx], self.tex_files[idx + 1] = self.tex_files[idx + 1], self.tex_files[idx]

        # 更新列表框
        filename = self.file_listbox.get(idx)
        self.file_listbox.delete(idx)
        self.file_listbox.insert(idx + 1, filename)
        self.file_listbox.selection_set(idx + 1)

    def select_bibitem_file(self):
        file = filedialog.askopenfilename(
            title="选择BibItem文件",
            filetypes=[("TEX文件", "*.tex"), ("BBL文件", "*.bbl"), ("所有文件", "*.*")]
        )
        if file:
            self.bibitem_file = file
            self.bibitem_file_text.delete(0, tk.END)
            self.bibitem_file_text.insert(0, file)

    def extract_cite_ids(self, tex_content):
        # 提取\cite{...}引用
        cite_pattern = r'\\cite\{([^\}]+)\}'
        matches = re.findall(cite_pattern, tex_content)

        # 处理多引用情况（逗号分隔）
        all_ids = []
        for match in matches:
            ids = [id.strip() for id in match.split(',')]
            all_ids.extend(ids)

        return all_ids

    def extract_bibitems(self, bibitem_content):
        # 获取所有bibitem ID
        id_pattern = r'\\bibitem\{([^\}]+)\}'
        id_matches = re.findall(id_pattern, bibitem_content)

        # 找到所有bibitem的起始位置
        bibitem_positions = []
        for match in re.finditer(r'\\bibitem\{', bibitem_content):
            bibitem_positions.append(match.start())

        bibitem_dict = {}
        for i in range(len(id_matches)):
            start_pos = bibitem_positions[i]
            if i < len(bibitem_positions) - 1:
                end_pos = bibitem_positions[i + 1]
                bibitem_text = bibitem_content[start_pos:end_pos]
            else:
                bibitem_text = bibitem_content[start_pos:]

            bibitem_dict[id_matches[i]] = bibitem_text

        return bibitem_dict

    def process_files(self):
        # 重置状态
        self.result_text.delete(1.0, tk.END)
        self.bibtex_id_list = []
        self.bibitem_dict = {}

        if not self.tex_files:
            messagebox.showerror("错误", "请至少选择一个TEX文件。")
            return

        if not self.bibitem_file:
            messagebox.showerror("错误", "请选择一个BibItem文件。")
            return

        self.status_var.set("从TEX文件中提取引用...")
        self.root.update()

        # 从TEX文件中提取引用
        for tex_file in self.tex_files:
            try:
                with open(tex_file, 'r', encoding='utf-8') as f:
                    tex_content = f.read()
                    ids = self.extract_cite_ids(tex_content)
                    self.bibtex_id_list.extend(ids)
            except Exception as e:
                messagebox.showerror("错误", f"读取{tex_file}时出错: {str(e)}")
                self.status_var.set("处理文件时出错")
                return

        self.status_var.set("从bibitem文件中提取条目...")
        self.root.update()

        # 从BibItem文件提取条目
        try:
            with open(self.bibitem_file, 'r', encoding='utf-8') as f:
                bibitem_content = f.read()
                self.bibitem_dict = self.extract_bibitems(bibitem_content)
        except Exception as e:
            messagebox.showerror("错误", f"读取{self.bibitem_file}时出错: {str(e)}")
            self.status_var.set("处理文件时出错")
            return

        self.status_var.set("对bibitem条目排序和去重...")
        self.root.update()

        # 排序和去重bibitem条目
        sorted_bibitems = []
        processed_ids = set()
        missing_ids = []

        for bibitem_id in self.bibtex_id_list:
            if bibitem_id in self.bibitem_dict and bibitem_id not in processed_ids:
                sorted_bibitems.append(self.bibitem_dict[bibitem_id])
                processed_ids.add(bibitem_id)
            elif bibitem_id not in self.bibitem_dict and bibitem_id not in missing_ids:
                missing_ids.append(bibitem_id)

        # 显示结果
        for bibitem in sorted_bibitems:
            self.result_text.insert(tk.END, f"{bibitem}\n")

        # 报告统计信息
        status_msg = f"处理完成: 从{len(self.bibtex_id_list)}个引用中提取了{len(sorted_bibitems)}个条目"
        if missing_ids:
            status_msg += f" | 缺失: {len(missing_ids)}个条目"
            missing_str = ", ".join(missing_ids[:5])
            if len(missing_ids) > 5:
                missing_str += "..."
            status_msg += f" ({missing_str})"

        self.status_var.set(status_msg)

        # 显示成功信息
        messagebox.showinfo("成功",
                            f"找到{len(self.bibtex_id_list)}个引用。\n"
                            f"处理了{len(sorted_bibitems)}个唯一bibitem条目。\n"
                            f"在bibitem文件中缺少{len(missing_ids)}个条目。")

    def save_results(self):
        if not self.result_text.get(1.0, tk.END).strip():
            messagebox.showerror("错误", "没有可以保存的结果。")
            return

        file = filedialog.asksaveasfilename(
            title="保存排序后的BibItems",
            defaultextension=".tex",
            filetypes=[("TEX文件", "*.tex"), ("BBL文件", "*.bbl"), ("文本文件", "*.txt"), ("所有文件", "*.*")]
        )

        if file:
            try:
                with open(file, 'w', encoding='utf-8') as f:
                    f.write(self.result_text.get(1.0, tk.END))
                messagebox.showinfo("成功", f"结果已保存到 {file}")
                self.status_var.set(f"结果已保存到 {os.path.basename(file)}")
            except Exception as e:
                messagebox.showerror("错误", f"保存文件时出错: {str(e)}")
                self.status_var.set("保存文件时出错")


def main():
    root = tk.Tk()
    app = BibTexSorterApp(root)
    root.mainloop()


if __name__ == "__main__":
    main()